<?php

/**
 * Simple Machines Forum (SMF) 
 *
 * @package testsuite
 * @author Simple Machines
 * @copyright 2011 Simple Machines
 * @license http://www.simplemachines.org/about/smf/license.php BSD
 *
 * @version 2.0
 */

if (!defined('SMF'))
	die ('Hacking Attempt...');

/*
 * Queries the list of all active projects and returns the data as an array.
 */
function TS_requestProjects()
{
	global $smcFunc, $scripturl, $context, $txt;
	
	$request = $smcFunc['db_query']('', '
		SELECT 
			p.id_project, p.project_name, p.description, p.id_member, p.poster_name, p.poster_time, p.poster_email, p.modified_time, p.modified_by
		FROM {db_prefix}testsuite_projects as p
		ORDER BY id_project'
	);

	$projects = array();
	while ($row = $smcFunc['db_fetch_assoc']($request))
	{
		$row['description'] = parse_bbc($row['description'], 0, $row['id_project']);

		$projects[$row['id_project']] = array(
			'id' => $row['id_project'],
			'name' => $row['project_name'],
			'description' => $row['description'],
			'link' => '<a href="' . $scripturl . '?action=testsuite;project=' . $row['id_project'] . '" target="_self">' . $row['project_name'] . '</a>',
			'member' => array(
				'id' => $row['id_member'],
				'username' => $row['poster_name'] != '' ? $row['poster_name'] : $txt['not_applicable'],
				'href' => $row['poster_name'] != '' && !empty($row['id_member']) ? $scripturl . '?action=profile;u=' . $row['id_member'] : '',
				'link' => $row['poster_name'] != '' ? (!empty($row['id_member']) ? '<a href="' . $scripturl . '?action=profile;u=' . $row['id_member'] . '">' . $row['poster_name'] . '</a>' : $row['poster_name']) : $txt['not_applicable'],
			),
			'modified_time' => timeformat($row['modified_time']),
			'modified_by' => $row['modified_by'],
		);
	}

	$smcFunc['db_free_result']($request);

	return $projects;
}

/*
 *  Queries the database for all suites for current project. Returns data as an array.
 */
function TS_loadProject($project_id, $load_suites = true)
{
	global $smcFunc, $scripturl, $context, $txt;

	if (empty($project_id))
	    fatal_lang_error("ts_no_project");

	$request = $smcFunc['db_query']('', '
		SELECT 
			p.id_project, p.project_name, p.description, p.id_member, p.poster_name, p.poster_time, p.poster_email, p.modified_time, p.modified_by
		FROM {db_prefix}testsuite_projects as p
		WHERE id_project = {int:current_project}
		LIMIT 1',
		array(
			'current_project' => $project_id,
		)
	);

	// Was the current project not found in the database?
	if ($smcFunc['db_num_rows']($request) == 0)
	{
		fatal_lang_error('ts_no_project', false);
	}

	$project = array();
	while ($row = $smcFunc['db_fetch_assoc']($request))
	{
		$row['description'] = parse_bbc($row['description'], 0, $row['id_project']);

		$project = array(
			'id' => $row['id_project'],
			'name' => $row['project_name'],
			'description' => $row['description'],
			'link' => '<a href="' . $scripturl . '?action=testsuite;project=' . $row['id_project'] . '" target="_self">' . $row['project_name'] . '</a>',
			'member' => array(
				'id' => $row['id_member'],
				'username' => $row['poster_name'] != '' ? $row['poster_name'] : $txt['not_applicable'],
				'href' => $row['poster_name'] != '' && !empty($row['id_member']) ? $scripturl . '?action=profile;u=' . $row['id_member'] : '',
				'link' => $row['poster_name'] != '' ? (!empty($row['id_member']) ? '<a href="' . $scripturl . '?action=profile;u=' . $row['id_member'] . '">' . $row['poster_name'] . '</a>' : $row['poster_name']) : $txt['not_applicable'],
			),
			'modified_time' => timeformat($row['modified_time']),
			'modified_by' => $row['modified_by'],
			'suites' => array(),
		);
	}

	$smcFunc['db_free_result']($request);

	if ($load_suites)
	{
	    // Grab the sweets from the candy store, depending on the ID of the project.
	    $request = $smcFunc['db_query']('', '
		    SELECT 
				s.id_suite, s.id_project, s.suite_name, s.description, s.id_member, s.poster_name, s.poster_time, s.poster_email, s.count, s.fail_count, s.modified_time, s.modified_by
		    FROM {db_prefix}testsuite_suites as s
			WHERE id_project = {int:current_project}
		    ORDER BY id_suite',
		    array(
			    'current_project' => $project_id,
		    )
	    );
	    while ($row = $smcFunc['db_fetch_assoc']($request))
	    {
			    $row['description'] = parse_bbc($row['description'], 0, $row['id_suite']);

			    $project['suites'][$row['id_suite']] = array(
				    'id' => $row['id_suite'],
				    'id_project' => $row['id_project'],
				    'name' => $row['suite_name'],
				    'description' => $row['description'],
				    'count' => $row['count'],
				    'count_link' =>'<a href="' . $scripturl . '?action=testsuite;runsforsuite=' . $row['id_suite'] . ';result=' . $txt['total'] .'" target="_self">' . $row['count'] . '</a>',
				    'pass_count' => ($row['count'] - $row['fail_count']),
				    'pass_count_link' =>'<a href="' . $scripturl . '?action=testsuite;runsforsuite=' . $row['id_suite'] . ';result=pass" target="_self">' . ($row['count'] - $row['fail_count']) . '</a>',
				    'fail_count' => $row['fail_count'],
				    'fail_count_link' =>'<a href="' . $scripturl . '?action=testsuite;runsforsuite=' . $row['id_suite'] . ';result=' . $txt['fail'] .'" target="_self">' . $row['fail_count'] . '</a>',
				    'link' => '<a href="' . $scripturl . '?action=testsuite;suite=' . $row['id_suite'] . '" target="_self">' . $row['suite_name'] . '</a>',
				    'member' => array(
					    'id' => $row['id_member'],
					    'username' => $row['poster_name'] != '' ? $row['poster_name'] : $txt['not_applicable'],
					    'href' => $row['poster_name'] != '' && !empty($row['id_member']) ? $scripturl . '?action=profile;u=' . $row['id_member'] : '',
					    'link' => $row['poster_name'] != '' ? (!empty($row['id_member']) ? '<a href="' . $scripturl . '?action=profile;u=' . $row['id_member'] . '">' . $row['poster_name'] . '</a>' : $row['poster_name']) : $txt['not_applicable'],
				    ),
					'modified_time' => timeformat($row['modified_time']),
					'modified_by' => $row['modified_by'],
			    );
	    }

	    $smcFunc['db_free_result']($request);
	}

	return $project;
}

function TS_loadSuite($suite_id, $load_cases = true)
{
	global $smcFunc, $scripturl, $context;

	if (empty($suite_id))
	    fatal_lang_error("ts_no_suite");

	$suite = array();

	// Load just the one suite that we are on. Don't need to load all suites when we don't need them.
	$request = $smcFunc['db_query']('', '
		SELECT 
			p.id_project, p.project_name, s.id_suite, s.suite_name, s.description
		FROM {db_prefix}testsuite_suites as s
			INNER JOIN {db_prefix}testsuite_projects as p ON p.id_project = s.id_project
		WHERE s.id_suite = {int:current_suite}
		ORDER BY id_suite
		LIMIT 1',
		array(
			'current_suite' => $suite_id,
		)
	);

	while ($row = $smcFunc['db_fetch_assoc']($request))
	{
		$suite = array(
			'id_project' => $row['id_project'],
			'project_name' => $row['project_name'],
			'id_suite' => $row['id_suite'],
			'name' => $row['suite_name'],
			'description' => $row['description'], 
			'link' => '<a href="' . $scripturl . '?action=testsuite;suite=' . $row['id_suite'] . '" target="_self">' . $row['suite_name'] . '</a>',
			'cases' => array(),
		);
	}

	// Was the current project not found in the database?
	if ($smcFunc['db_num_rows']($request) == 0)
	{
		fatal_lang_error('ts_no_suite', false);
	}

	$smcFunc['db_free_result']($request);

	if ($load_cases)
	{
	    $request = $smcFunc['db_query']('', '
		    SELECT c.id_case, c.id_suite, c.case_name, c.description, c.steps, c.expected_result, c.id_member, c.poster_name, c.poster_time, c.poster_email, c.id_assigned, c.modified_time, c.modified_by, count, c.fail_count
		    FROM {db_prefix}testsuite_cases as c
		    WHERE id_suite = {int:current_suite}
		    ORDER BY id_case',
		    array(
			    'current_suite' => $suite_id,
		    )
	    );
	    $suite['cases'] = array();
	
	    while ($row = $smcFunc['db_fetch_assoc']($request))
	    {
		    $row['description'] = parse_bbc($row['description'], 1, $row['id_case']);
		    $row['steps'] = parse_bbc($row['steps'], 0, $row['id_case']);

		    $suite['cases'][$row['id_case']] = array(
				'id' => $row['id_case'],
				'id_suite' => $row['id_suite'],
				'name' => $row['case_name'],
				'description' => $row['description'],
				'steps' => $row['steps'],
				'expected_result' => $row['expected_result'],
				'id_assigned' => $row['id_assigned'],
				'count' => $row['count'],
				'fail_count' => $row['fail_count'],
				'pass_count' => ($row['count'] - $row['fail_count']),
				'link' => '<a href="' . $scripturl . '?action=testsuite;case=' . $row['id_case'] . '" target="_self">' . $row['case_name'] . '</a>',
				'member' => array(
					'id' => $row['id_member'],
					'username' => $row['poster_name'] != '' ? $row['poster_name'] : $txt['not_applicable'],
					'href' => $row['poster_name'] != '' && !empty($row['id_member']) ? $scripturl . '?action=profile;u=' . $row['id_member'] : '',
					'link' => $row['poster_name'] != '' ? (!empty($row['id_member']) ? '<a href="' . $scripturl . '?action=profile;u=' . $row['id_member'] . '">' . $row['poster_name'] . '</a>' : $row['poster_name']) : $txt['not_applicable'],
				),
				'modified_time' => timeformat($row['modified_time']),
				'modified_by' => $row['modified_by'],
			);
	    }
	    $smcFunc['db_free_result']($request);
	}

	return $suite;
}

function TS_loadCase($case_id, $load_runs = true)
{
	global $smcFunc, $scripturl, $context;
	
	// Load just the current case.
	$request = $smcFunc['db_query']('', '
		SELECT c.id_case, p.project_name, p.id_project, c.id_suite, s.suite_name, c.case_name, c.description, c.steps, c.expected_result, c.id_member, c.poster_name, c.poster_time, c.poster_email, c.id_assigned, c.modified_time, c.modified_by, c.count, c.fail_count
		FROM {db_prefix}testsuite_cases as c
		INNER JOIN {db_prefix}testsuite_suites as s ON (c.id_suite = s.id_suite)
		INNER JOIN {db_prefix}testsuite_projects as p ON (s.id_project = p.id_project)
		WHERE id_case = {int:id_case}
		ORDER BY id_case
		LIMIT 1',
		array(
			'id_case' => $case_id,
		)
	);

	$case = array();
	
	while ($row = $smcFunc['db_fetch_assoc']($request))
	{
		$row['description'] = parse_bbc($row['description'], 1, $row['id_case']);
		$row['steps'] = parse_bbc($row['steps'], 1, $row['id_case']);
		$row['expected_result'] = parse_bbc($row['expected_result'], 1, $row['id_case']);

		$case = array(
				'id' => $row['id_case'],
				'id_suite' => $row['id_suite'],
				'id_project' => $row['id_project'],
				'name' => $row['case_name'],
				'suite_name' => $row['suite_name'],
				'project_name' => $row['project_name'],
				'description' => $row['description'],
				'steps' => $row['steps'],
				'expected_result' => $row['expected_result'],
				'id_assigned' => $row['id_assigned'],
				'count' => $row['count'],
				'fail_count' => $row['fail_count'],
				'pass_count' => ($row['count'] - $row['fail_count']),
				'link' => '<a href="' . $scripturl . '?action=testsuite;case=' . $row['id_case'] . '" target="_self">' . $row['case_name'] . '</a>',
				'member' => array(
					'id' => $row['id_member'],
					'username' => $row['poster_name'] != '' ? $row['poster_name'] : $txt['not_applicable'],
					'href' => $row['poster_name'] != '' && !empty($row['id_member']) ? $scripturl . '?action=profile;u=' . $row['id_member'] : '',
					'link' => $row['poster_name'] != '' ? (!empty($row['id_member']) ? '<a href="' . $scripturl . '?action=profile;u=' . $row['id_member'] . '">' . $row['poster_name'] . '</a>' : $row['poster_name']) : $txt['not_applicable'],
				),
				'modified_time' => timeformat($row['modified_time']),
				'modified_by' => $row['modified_by'],
				'runs' => array(),
			);
	}
	
	if ($smcFunc['db_num_rows']($request) <= 0)
	{
		fatal_lang_error('ts_no_case');
	}
	
	$smcFunc['db_free_result']($request);
	
	$request = $smcFunc['db_query']('', '
		SELECT id_run, id_case, result_achieved, feedback, id_member, poster_name, poster_time, poster_email, id_bug, modified_time, modified_by
		FROM {db_prefix}testsuite_runs
		WHERE id_case = {int:current_case}
		ORDER BY id_run',
		array(
			'current_case' => $case_id,
		)
	);
	$context['test_suite']['run'] = array();
	
	while ($row = $smcFunc['db_fetch_assoc']($request))
	{
		$row['feedback'] = parse_bbc($row['feedback'], 0, $row['id_run']);

		$case['runs'][$row['id_run']] = array(
			'id' => $row['id_run'],
			'id_case' => $row['id_case'],
			'result_achieved' => $row['result_achieved'],
			'feedback' => $row['feedback'],
			'id_bug' => $row['id_bug'],
			'href' => $scripturl . '?action=testsuite;case=' . $row['id_case'] . ';#run' . $row['id_run'],
			'link' => '<a href="' . $scripturl . '?action=testsuite;case=' . $row['id_case'] . ';#run' . $row['id_run'] . '">' . $row['id_run'] . '</a>',
			'time' => timeformat($row['poster_time']),
			'member' => array(
				'id' => $row['id_member'],
				'username' => $row['poster_name'] != '' ? $row['poster_name'] : $txt['not_applicable'],
				'href' => $row['poster_name'] != '' && !empty($row['id_member']) ? $scripturl . '?action=profile;u=' . $row['id_member'] : '',
				'link' => $row['poster_name'] != '' ? (!empty($row['id_member']) ? '<a href="' . $scripturl . '?action=profile;u=' . $row['id_member'] . '">' . $row['poster_name'] . '</a>' : $row['poster_name']) : $txt['not_applicable'],
			),
			'modified_time' => timeformat($row['modified_time']),
			'modified_by' => $row['modified_by'],
		);
	}
	$smcFunc['db_free_result']($request);

	return $case;
}

function TS_loadRun($run_id)
{
	global $smcFunc, $scripturl, $context;
	
	// Load just the current case.
	$request = $smcFunc['db_query']('', '
		SELECT 
			id_run, id_case, result_achieved, feedback, id_member, poster_name, poster_time, poster_email, id_bug, modified_time, modified_by
		FROM {db_prefix}testsuite_runs
		WHERE id_run = {int:id_run}
		LIMIT 1',
		array(
			'id_run' => $run_id,
		)
	);

	$run = array();
	
	while ($row = $smcFunc['db_fetch_assoc']($request))
	{
		$row['feedback'] = parse_bbc($row['feedback'], 0, $row['id_case']);

		$run = array(
			'id' => $row['id_run'],
			'id_case' => $row['id_case'],
			'result_achieved' => $row['result_achieved'],
			'feedback' => $row['feedback'],
			'id_bug' => $row['id_bug'],
			'href' => $scripturl . '?action=testsuite;case=' . $row['id_case'] . ';#run' . $row['id_run'],
			'link' => '<a href="' . $scripturl . '?action=testsuite;case=' . $row['id_case'] . ';#run' . $row['id_run'] . '">' . $row['id_run'] . '</a>',
			'time' => timeformat($row['poster_time']),
			'member' => array(
				'id' => $row['id_member'],
				'username' => $row['poster_name'] != '' ? $row['poster_name'] : $txt['not_applicable'],
				'href' => $row['poster_name'] != '' && !empty($row['id_member']) ? $scripturl . '?action=profile;u=' . $row['id_member'] : '',
				'link' => $row['poster_name'] != '' ? (!empty($row['id_member']) ? '<a href="' . $scripturl . '?action=profile;u=' . $row['id_member'] . '">' . $row['poster_name'] . '</a>' : $row['poster_name']) : $txt['not_applicable'],
			),
			'modified_time' => timeformat($row['modified_time']),
			'modified_by' => $row['modified_by'],
		);
	}
	
	if ($smcFunc['db_num_rows']($request) <= 0)
	{
		fatal_lang_error('ts_no_case');
	}
	
	$smcFunc['db_free_result']($request);

	return $run;
}

function TS_requestRunsforSuites()
{
	global $smcFunc, $scripturl, $context;
	
	$suite_id = $context['test_suite']['current_suite'];
	$result = isset($_REQUEST['result']) ? strtolower($_REQUEST['result']) : '';

	$request = $smcFunc['db_query']('', '
		SELECT 
			r.id_run, r.id_case, c.case_name
		FROM {db_prefix}testsuite_runs AS r
			INNER JOIN {db_prefix}testsuite_cases AS c ON (c.id_case = r.id_case)
		WHERE c.id_suite = {int:current_id_suite}
			AND r.result_achieved = {string:result_achieved}
		ORDER by r.id_run',
		array(
			'current_id_suite' => $suite_id,
			'result_achieved' => $result,
		)
	);
	$context['test_suite']['run_links'] = array();
	
	while ($row = $smcFunc['db_fetch_assoc']($request))
	{
		$context['test_suite']['run_links'][$row['id_run']] = array(
			'id' => $row['id_run'],
			'link' => '<a href="' . $scripturl . '?action=testsuite;case=' . $row['id_case'] . ';#run=' . $row['id_run'] . '">' . $row['id_run'] . '</a>',
			'id_case' => $row['id_case'],
			'case_name' => $row['case_name'],
			'href' => $scripturl . '?action=testsuite;suite=' . $suite_id . ';case=' . $row['id_case'],
		);
	}
	$smcFunc['db_free_result']($request);

	$request = $smcFunc['db_query']('', '
		SELECT 
			r.id_run, r.id_case, c.case_name
		FROM {db_prefix}testsuite_runs AS r
			INNER JOIN {db_prefix}testsuite_cases AS c ON (c.id_case = r.id_case)
		WHERE c.id_suite = {int:current_id_suite}
		ORDER by r.id_run',
		array(
			'current_id_suite' => $suite_id,
		)
	);
	$context['test_suite']['total_run_links'] = array();
	
	while ($row = $smcFunc['db_fetch_assoc']($request))
	{
		$context['test_suite']['total_run_links'][$row['id_run']] = array(
			'id' => $row['id_run'],
			'link' => '<a href="' . $scripturl . '?action=testsuite;case=' . $row['id_case'] . '#run=' . $row['id_run'] . '">' . $row['id_run'] . '</a>',
			'id_case' => $row['id_case'],
			'case_name' => $row['case_name'],
			'href' => $scripturl . '?action=testsuite;case=' . $row['id_case'],
		);
	}
	$smcFunc['db_free_result']($request);

}

function TS_createProject(&$projectOption, &$posterOptions)
{
	global $user_info, $smcFunc, $context;

	$projectOption['id'] = empty($projectOption['id']) ? 0 : (int) $projectOption['id'];
	$posterOptions['id'] = empty($posterOptions['id']) ? 0 : (int) $posterOptions['id'];
	$posterOptions['name'] = $user_info['name'];
	$posterOptions['email'] = $user_info['email'];
	
	// Insert the Project.
	$smcFunc['db_insert']('',
		'{db_prefix}testsuite_projects',
		array(
			'id_project' => 'int', 'id_member' => 'int', 'project_name' => 'string-255', 'description' => 'string-65534',
			'poster_name' => 'string-255', 'poster_email' => 'string-255', 'poster_time' => 'int',
		),
		array(
			$projectOption['id'], $posterOptions['id'], $projectOption['project_name'], $projectOption['description'],
			$posterOptions['name'], $posterOptions['email'], time(),
		),
		array('project')
	);
	$projectOption['id'] = $smcFunc['db_insert_id']('{db_prefix}testsuite_projects', 'project');

	// Something went wrong creating the Project...
	if (empty($projectOption['id']))
		return false;
		
	// Success.
	return true;
}

// Precondition: project_id belongs to a project that hasn't been deleted.
/* 	$remove_level is the hierarchy of what gets removed.
	Level 0 will just remove the project.
	Level 1 will also remove associated suites.
	Level 2 will remove the associated suites and the cases that belong to them.
	Level 3 will remove everything including the runs associated to those cases nested in the project hierarchy.
*/
function TS_removeProject($project_ids, $remove_level = 0)
{
	global $smcFunc;

	if (!is_array($project_ids))
		$project_ids = array($project_ids);
	
	$smcFunc['db_query']('', '
		DELETE FROM {db_prefix}testsuite_projects
		WHERE id_project IN ({array_int:project_ids})',
		array(
			'project_ids' => $project_ids,
		)
	);
	
	// !!! Disable for now until I can test and finish this.
	if ($remove_level > 0 && 0)
	{
		$suite_ids = array();
		foreach ($project_ids as $id)
		{
			$project = TS_loadProject($id);
			
			foreach($project['suites'] as $suite)
			{
				$suite_ids[] = $suite['id'];
			}
		}
		TS_removeSuite($suite_ids, $removeLevel - 1);
	}	
}

function TS_createSuite(&$suiteOptions, &$posterOptions)
{
	global $user_info, $smcFunc, $context;

	$suiteOptions['id'] = empty($suiteOptions['id']) ? 0 : (int) $suiteOptions['id'];
	$suiteOptions['id_project'] = empty($suiteOptions['id_project']) ? 0 : (int) $suiteOptions['id_project'];
	$posterOptions['id'] = empty($posterOptions['id']) ? 0 : (int) $posterOptions['id'];
	$posterOptions['name'] = $user_info['name'];
	$posterOptions['email'] = $user_info['email'];
	
	// Insert the Suite.
	$smcFunc['db_insert']('',
		'{db_prefix}testsuite_suites',
		array(
			'id_suite' => 'int', 'id_project' =>  'int', 'suite_name' => 'string-255', 'description' => 'string-65534',
			'poster_name' => 'string-255', 'poster_email' => 'string-255', 'id_member' => 'int', 'poster_time' => 'int',
		),
		array(
			$suiteOptions['id'], $suiteOptions['id_project'], $suiteOptions['suite_name'], $suiteOptions['description'],
			$posterOptions['name'], $posterOptions['email'], $posterOptions['id'], time(),
		),
		array('suite')
	);
	$suiteOptions['id'] = $smcFunc['db_insert_id']('{db_prefix}testsuite_suites', 'suite');

	// Something went wrong creating the Suite...
	if (empty($suiteOptions['id']))
		return false;
		
	// Success.
	return true;
}

// Precondition: suite_ids belongs to a suite that hasn't been deleted.
/* 	$remove_level is the hierarchy of what gets removed.
	Level 0 will just remove the suite.
	Level 1 will remove the associated suites and the cases that belong to them.
	Level 2 will remove everything including the runs associated to those cases nested in the project hierarchy.
*/
function TS_removeSuite($suite_ids, $remove_level = 0)
{
	global $smcFunc;

	if (!is_array($suite_ids))
		$suite_ids = array($suite_ids);
	
	$smcFunc['db_query']('', '
		DELETE FROM {db_prefix}testsuite_suites
		WHERE id_suite IN ({array_int:suite_ids})',
		array(
			'suite_ids' => $suite_ids,
		)
	);
	
	// !!! TBD $remove_level.
	// Use chain of command.
	// Call TS_removeCase($case_id, $remove_level - 1);
}

function TS_createCase(&$caseOption, &$posterOptions)
{
	global $user_info, $smcFunc, $context;

	$caseOption['id'] = empty($caseOption['id']) ? 0 : (int) $caseOption['id'];
	$caseOption['id_suite'] = empty($caseOption['id_suite']) ? 0 : (int) $caseOption['id_suite'];
	$posterOptions['id'] = empty($posterOptions['id']) ? 0 : (int) $posterOptions['id'];
	$posterOptions['name'] = $user_info['name'];
	$posterOptions['email'] = $user_info['email'];
	$posterOptions['id_assigned'] = empty($posterOptions['id_assigned']) ? 0 : (int) $posterOptions['id_assigned'];

	// Insert the Test Case.
	$smcFunc['db_insert']('',
		'{db_prefix}testsuite_cases',
		array(
			'id_case' => 'int', 'id_suite' => 'int', 'case_name' => 'string-255', 'description' => 'string-65534', 'steps' => 'string-65534',
			'expected_result' => 'string-65534',
			'poster_name' => 'string-255', 'poster_email' => 'string-255', 'id_member' => 'int', 'poster_time' => 'int', 'id_assigned' => 'int',
		),
		array(
			$caseOption['id'], $caseOption['id_suite'], $caseOption['case_name'], $caseOption['description'], $caseOption['steps'],
			$caseOption['expected_result'],
			$posterOptions['name'], $posterOptions['email'], $posterOptions['id'], time(), $posterOptions['id_assigned'],
		),
		array('case')
	);
	$caseOption['id'] = $smcFunc['db_insert_id']('{db_prefix}testsuite_cases', 'case');

	// Something went wrong creating the Test Case...
	if (empty($caseOption['id']))
		return false;
		
	// Success.
	return true;
}

// Precondition: case_id belongs to a case that hasn't been deleted.
// Precondition: suite_id belongs to a suite that hasn't been deleted.
/* 	$remove_level is the hierarchy of what gets removed.
	Level 0 will just remove the case.
	Level 1 will remove the runs that belong to the case.
*/
function TS_removeCase($case_ids, $remove_assoc_runs = false)
{
	global $smcFunc;
	
	if (!is_array($case_ids))
		$case_ids = array($case_ids);

	$smcFunc['db_query']('', '
		DELETE FROM {db_prefix}testsuite_cases
		WHERE id_case IN ({array_int:case_ids})',
		array(
			'case_ids' => $case_ids,
		)
	);
	
	// !!! TBD $remove_level.
	// Use chain of command.
	// Call TS_removeSuite($run_id, $remove_level - 1);
}

function TS_createRun(&$runOption, &$posterOptions)
{
	global $user_info, $smcFunc, $context;

	$runOption['id'] = empty($runOption['id']) ? 0 : (int) $runOption['id'];
	$runOption['id_case'] = empty($runOption['id_case']) ? 0 : (int) $runOption['id_case'];
	$posterOptions['id'] = empty($posterOptions['id']) ? 0 : (int) $posterOptions['id'];
	$posterOptions['name'] = $user_info['name'];
	$posterOptions['email'] = $user_info['email'];
	
	// Insert the Run.
	$smcFunc['db_insert']('',
		'{db_prefix}testsuite_runs',
		array(
			'id_run' => 'int', 'id_case' => 'int', 'result_achieved' => 'string-255', 'feedback' => 'string-65534', 'id_bug' => 'int',
			'poster_name' => 'string-255', 'poster_email' => 'string-255', 'id_member' => 'int', 'poster_time' => 'int',
		),
		array(
			$runOption['id'], $runOption['id_case'], $runOption['result_achieved'], $runOption['feedback'], $runOption['id_bug'],
			$posterOptions['name'], $posterOptions['email'], $posterOptions['id'], time(),
		),
		array('run')
	);
	$runOption['id'] = $smcFunc['db_insert_id']('{db_prefix}testsuite_runs', 'run');

	// Something went wrong creating the Run...
	if (empty($runOption['id']))
		return false;
		
	// Success.
	return true;
}

// Precondition: run_id belongs to a run that hasn't been deleted.
function TS_removeRun($run_ids)
{
	global $smcFunc;

	if (!is_array($run_ids))
		$run_ids = array($run_ids);
	
	$smcFunc['db_query']('', '
		DELETE FROM {db_prefix}testsuite_runs
		WHERE id_run IN ({array_int:run_ids})',
		array(
			'run_ids' => $run_ids,
		)
	);
}

function TS_modifyProject(&$projectOptions, &$posterOptions)
{
	global $user_info, $smcFunc, $context;

	// This is longer than it has to be, but makes it so we only set/change what we have to.
	$project_columns = array();
	if (isset($projectOptions['project_name']))
		$project_columns['project_name'] = $projectOptions['project_name'];
	if (isset($projectOptions['description']))
		$project_columns['description'] = $projectOptions['description'];

	if (!empty($projectOptions['modify_time']))
	{
		$project_columns['modified_time'] = $projectOptions['modify_time'];
		$project_columns['modified_by'] = $projectOptions['modified_by'];
	}

	// Which columns need to be ints?
	$projectInts = array('modified_time');
	$update_parameters = array(
		'id_project' => $projectOptions['id'],
	);

	foreach ($project_columns as $var => $val)
	{
		$project_columns[$var] = $var . ' = {' . (in_array($var, $projectInts) ? 'int' : 'string') . ':var_' . $var . '}';
		$update_parameters['var_' . $var] = $val;
	}

	// Nothing to do?
	if (empty($project_columns))
		return true;

	// Change the post.
	$smcFunc['db_query']('', '
		UPDATE {db_prefix}testsuite_projects
		SET ' . implode(', ', $project_columns) . '
		WHERE id_project = {int:id_project}',
		$update_parameters
	);

	return true;
}

function TS_modifySuite(&$suiteOptions, &$posterOptions)
{
	global $user_info, $smcFunc, $context;

	// This is longer than it has to be, but makes it so we only set/change what we have to.
	$suite_columns = array();
	if (isset($suiteOptions['suite_name']))
		$suite_columns['suite_name'] = $suiteOptions['suite_name'];
	if (isset($suiteOptions['description']))
		$suite_columns['description'] = $suiteOptions['description'];

	if (!empty($suiteOptions['modify_time']))
	{
		$suite_columns['modified_time'] = $suiteOptions['modify_time'];
		$suite_columns['modified_by'] = $suiteOptions['modified_by'];
	}

	// Which columns need to be ints?
	$suiteInts = array('modified_time');
	$update_parameters = array(
		'id_suite' => $suiteOptions['id'],
	);

	foreach ($suite_columns as $var => $val)
	{
		$suite_columns[$var] = $var . ' = {' . (in_array($var, $suiteInts) ? 'int' : 'string') . ':var_' . $var . '}';
		$update_parameters['var_' . $var] = $val;
	}

	// Nothing to do?
	if (empty($suite_columns))
		return true;

	// Change the post.
	$smcFunc['db_query']('', '
		UPDATE {db_prefix}testsuite_suites
		SET ' . implode(', ', $suite_columns) . '
		WHERE id_suite = {int:id_suite}',
		$update_parameters
	);

	return true;
}

function TS_modifyCase(&$caseOptions, &$posterOptions)
{
	global $user_info, $smcFunc, $context;

	// This is longer than it has to be, but makes it so we only set/change what we have to.
	$case_columns = array();
	if (isset($caseOptions['case_name']))
		$case_columns['case_name'] = $caseOptions['case_name'];
	if (isset($caseOptions['description']))
		$case_columns['description'] = $caseOptions['description'];
	if (isset($caseOptions['steps']))
		$case_columns['steps'] = $caseOptions['steps'];
	if (isset($caseOptions['expected_result']))
		$case_columns['expected_result'] = $caseOptions['expected_result'];
	if (isset($posterOptions['id_assigned']))
		$case_columns['id_assigned'] = $posterOptions['id_assigned'];

	if (!empty($caseOptions['modify_time']))
	{
		$case_columns['modified_time'] = $caseOptions['modify_time'];
		$case_columns['modified_by'] = $caseOptions['modified_by'];
	}

	// Which columns need to be ints?
	$caseInts = array('modified_time');
	$update_parameters = array(
		'id_case' => $caseOptions['id'],
	);

	foreach ($case_columns as $var => $val)
	{
		$case_columns[$var] = $var . ' = {' . (in_array($var, $caseInts) ? 'int' : 'string') . ':var_' . $var . '}';
		$update_parameters['var_' . $var] = $val;
	}

	// Nothing to do?
	if (empty($case_columns))
		return true;

	// Change the post.
	$smcFunc['db_query']('', '
		UPDATE {db_prefix}testsuite_cases
		SET ' . implode(', ', $case_columns) . '
		WHERE id_case = {int:id_case}',
		$update_parameters
	);

	return true;
}

function TS_modifyRun(&$runOptions, &$posterOptions)
{
	global $user_info, $smcFunc, $context;

	// This is longer than it has to be, but makes it so we only set/change what we have to.
	$run_columns = array();
	if (isset($runOptions['result_achieved']))
		$run_columns['result_achieved'] = $runOptions['result_achieved'];
	if (isset($runOptions['feedback']))
		$run_columns['feedback'] = $runOptions['feedback'];
	if (isset($runOptions['id_bug']))
		$run_columns['id_bug'] = $runOptions['id_bug'];

	if (!empty($runOptions['modify_time']))
	{
		$run_columns['modified_time'] = $runOptions['modify_time'];
		$run_columns['modified_by'] = $runOptions['modified_by'];
	}

	// Which columns need to be ints?
	$runInts = array('modified_time');
	$update_parameters = array(
		'id_run' => $runOptions['id'],
	);

	foreach ($run_columns as $var => $val)
	{
		$run_columns[$var] = $var . ' = {' . (in_array($var, $runInts) ? 'int' : 'string') . ':var_' . $var . '}';
		$update_parameters['var_' . $var] = $val;
	}

	// Nothing to do?
	if (empty($run_columns))
		return true;

	// Change the post.
	$smcFunc['db_query']('', '
		UPDATE {db_prefix}testsuite_runs
		SET ' . implode(', ', $run_columns) . '
		WHERE id_run = {int:id_run}',
		$update_parameters
	);

	return true;
}

// Creates a box that can be used for richedit stuff like BBC, Smileys etc.
function TS_editor_controller($editorOptions)
{
	global $txt, $modSettings, $smcFunc, $context, $settings, $user_info, $sourcedir, $scripturl;
	
	// Load the Post language file... for the moment at least.
	loadLanguage('Post');

	// Is this the first richedit - if so we need to ensure some template stuff is initialised.
	if (empty($context['controls']['richedit']))
	{
		// Some general stuff.
		$settings['smileys_url'] = $modSettings['smileys_url'] . '/' . $user_info['smiley_set'];

		// This really has some WYSIWYG stuff.
		loadTemplate('GenericControls', $context['browser']['is_ie'] ? 'editor_ie' : 'editor');
		$context['html_headers'] .= '
		<script type="text/javascript"><!-- // --><![CDATA[
			var smf_smileys_url = \'' . $settings['smileys_url'] . '\';
			var oEditorStrings= {
				wont_work: \'' . addcslashes($txt['rich_edit_wont_work'], "'") . '\',
				func_disabled: \'' . addcslashes($txt['rich_edit_function_disabled'], "'") . '\',
				prompt_text_url: \'' . addcslashes($txt['prompt_text_url'], "'") . '\',
				prompt_text_img: \'' . addcslashes($txt['prompt_text_img'], "'") . '\'
			}
		// ]]></script>
		<script type="text/javascript" src="' . $settings['default_theme_url'] . '/scripts/editor.js?rc5"></script>';
	}

	// Start off the editor...
	$context['controls']['richedit'][$editorOptions['id']] = array(
		'id' => $editorOptions['id'],
		'value' => $editorOptions['value'],
		'columns' => isset($editorOptions['columns']) ? $editorOptions['columns'] : 60,
		'rows' => isset($editorOptions['rows']) ? $editorOptions['rows'] : 12,
		'width' => isset($editorOptions['width']) ? $editorOptions['width'] : '70%',
		'height' => isset($editorOptions['height']) ? $editorOptions['height'] : '150px',
		'disable_smiley_box' => false,
		'form' => isset($editorOptions['form']) ? $editorOptions['form'] : 'postmodify',
		'bbc_level' => 'full',
		'rich_active' => false,
	);

	// Switch between default images and back... mostly in case you don't have an PersonalMessage template, but do have a Post template.
	if (isset($settings['use_default_images']) && $settings['use_default_images'] == 'defaults' && isset($settings['default_template']))
	{
		$temp1 = $settings['theme_url'];
		$settings['theme_url'] = $settings['default_theme_url'];

		$temp2 = $settings['images_url'];
		$settings['images_url'] = $settings['default_images_url'];

		$temp3 = $settings['theme_dir'];
		$settings['theme_dir'] = $settings['default_theme_dir'];
	}

	if (empty($context['bbc_tags']))
	{
		// The below array makes it dead easy to add images to this control. Add it to the array and everything else is done for you!
		$context['bbc_tags'] = array();
		$context['bbc_tags'][] = array(
			array(
				'image' => 'bold',
				'code' => 'b',
				'before' => '[b]',
				'after' => '[/b]',
				'description' => $txt['bold'],
			),
			array(
				'image' => 'italicize',
				'code' => 'i',
				'before' => '[i]',
				'after' => '[/i]',
				'description' => $txt['italic'],
			),
			array(
				'image' => 'underline',
				'code' => 'u',
				'before' => '[u]',
				'after' => '[/u]',
				'description' => $txt['underline']
			),
			array(
				'image' => 'strike',
				'code' => 's',
				'before' => '[s]',
				'after' => '[/s]',
				'description' => $txt['strike']
			),

			array(
				'image' => 'img',
				'code' => 'img',
				'before' => '[img]',
				'after' => '[/img]',
				'description' => $txt['image']
			),
			array(
				'image' => 'url',
				'code' => 'url',
				'before' => '[url]',
				'after' => '[/url]',
				'description' => $txt['hyperlink']
			),
			array(
				'image' => 'hr',
				'code' => 'hr',
				'before' => '[hr]',
				'description' => $txt['horizontal_rule']
			),
		);


		foreach ($context['bbc_tags'] as $row => $tagRow)
			$context['bbc_tags'][$row][count($tagRow) - 1]['isLast'] = true;
	}

	// Initialize smiley array... if not loaded before.
	if (empty($context['smileys']) && empty($editorOptions['disable_smiley_box']))
	{
		$context['smileys'] = array(
			'postform' => array(),
			'popup' => array(),
		);

		// Load smileys - don't bother to run a query if we're not using the database's ones anyhow.
		if (empty($modSettings['smiley_enable']) && $user_info['smiley_set'] != 'none')
			$context['smileys']['postform'][] = array(
				'smileys' => array(
					array(
						'code' => ':)',
						'filename' => 'smiley.gif',
						'description' => $txt['icon_smiley'],
					),
					array(
						'code' => ';)',
						'filename' => 'wink.gif',
						'description' => $txt['icon_wink'],
					),
					array(
						'code' => ':D',
						'filename' => 'cheesy.gif',
						'description' => $txt['icon_cheesy'],
					),
					array(
						'code' => ';D',
						'filename' => 'grin.gif',
						'description' => $txt['icon_grin']
					),
					array(
						'code' => '>:(',
						'filename' => 'angry.gif',
						'description' => $txt['icon_angry'],
					),
					array(
						'code' => ':(',
						'filename' => 'sad.gif',
						'description' => $txt['icon_sad'],
					),
					array(
						'code' => ':o',
						'filename' => 'shocked.gif',
						'description' => $txt['icon_shocked'],
					),
					array(
						'code' => '8)',
						'filename' => 'cool.gif',
						'description' => $txt['icon_cool'],
					),
					array(
						'code' => '???',
						'filename' => 'huh.gif',
						'description' => $txt['icon_huh'],
					),
					array(
						'code' => '::)',
						'filename' => 'rolleyes.gif',
						'description' => $txt['icon_rolleyes'],
					),
					array(
						'code' => ':P',
						'filename' => 'tongue.gif',
						'description' => $txt['icon_tongue'],
					),
					array(
						'code' => ':-[',
						'filename' => 'embarrassed.gif',
						'description' => $txt['icon_embarrassed'],
					),
					array(
						'code' => ':-X',
						'filename' => 'lipsrsealed.gif',
						'description' => $txt['icon_lips'],
					),
					array(
						'code' => ':-\\',
						'filename' => 'undecided.gif',
						'description' => $txt['icon_undecided'],
					),
					array(
						'code' => ':-*',
						'filename' => 'kiss.gif',
						'description' => $txt['icon_kiss'],
					),
					array(
						'code' => ':\'(',
						'filename' => 'cry.gif',
						'description' => $txt['icon_cry'],
						'isLast' => true,
					),
				),
				'isLast' => true,
			);
		elseif ($user_info['smiley_set'] != 'none')
		{
			if (($temp = cache_get_data('posting_smileys', 480)) == null)
			{
				$request = $smcFunc['db_query']('', '
					SELECT code, filename, description, smiley_row, hidden
					FROM {db_prefix}smileys
					WHERE hidden IN (0, 2)
					ORDER BY smiley_row, smiley_order',
					array(
					)
				);
				while ($row = $smcFunc['db_fetch_assoc']($request))
				{
					$row['filename'] = htmlspecialchars($row['filename']);
					$row['description'] = htmlspecialchars($row['description']);

					$context['smileys'][empty($row['hidden']) ? 'postform' : 'popup'][$row['smiley_row']]['smileys'][] = $row;
				}
				$smcFunc['db_free_result']($request);

				foreach ($context['smileys'] as $section => $smileyRows)
				{
					foreach ($smileyRows as $rowIndex => $smileys)
						$context['smileys'][$section][$rowIndex]['smileys'][count($smileys['smileys']) - 1]['isLast'] = true;

					if (!empty($smileyRows))
						$context['smileys'][$section][count($smileyRows) - 1]['isLast'] = true;
				}

				cache_put_data('posting_smileys', $context['smileys'], 480);
			}
			else
				$context['smileys'] = $temp;
		}
	}

	// Set a flag so the sub template knows what to do...
	$context['show_bbc'] = !empty($modSettings['enableBBC']) && !empty($settings['show_bbc']);

	// Generate a list of buttons that shouldn't be shown - this should be the fastest way to do this.
	$disabled_tags = array();
	if (!empty($modSettings['disabledBBC']))
		$disabled_tags = explode(',', $modSettings['disabledBBC']);

	// Switch the URLs back... now we're back to whatever the main sub template is.  (like folder in PersonalMessage.)
	if (isset($settings['use_default_images']) && $settings['use_default_images'] == 'defaults' && isset($settings['default_template']))
	{
		$settings['theme_url'] = $temp1;
		$settings['images_url'] = $temp2;
		$settings['theme_dir'] = $temp3;
	}
}

?>